#include "../include/socket.h"
#include <cerrno>
#include "../include/my_log.h"
#include "../include/macro.h"
#include <string.h>
#include "../include/global.h"
#include <stdio.h>
void _socket::accept_event_handler(connection_p con_p)
{
    int use_accept4=1;  //表示用accept4,能够顺带设置
    struct sockaddr_in cli_addr;    //用于接收客户端地址
    socklen_t len=sizeof(cli_addr);

    int new_fd=-1;
    while(1){
        new_fd=accept4(con_p->fd,(struct sockaddr *)&cli_addr,&len,SOCK_NONBLOCK);    //设置成非阻塞
        if(new_fd==-1){
            if(errno==ENOSYS){
                //表示accept没有实现
                perror("accept4 error:");
                use_accept4=0;
                break; 
            }
        }
        break;
    }
    if(use_accept4==0){
        new_fd=accept(con_p->fd,(struct sockaddr *)&cli_addr,&len);
        if(new_fd==-1){
            my_log * log=my_log::get_log_instance();
            log->write_log(LOG_ERR,errno,"让我接收连接，连接却接收失败！！！");
            return;
        }
    }
    //到这里表示接收好了
    //判断当前实际客户端的连接数量是否过大

    connection_p new_con_p=this->get_one_free_con();        //返回前，会初始化相关字段
    if(new_con_p==nullptr){
        //表示没有空闲连接，而且还申请不到内存了，直接释放掉,在此之前已经写日志了
        close(new_fd);
        add_connection_to_free(new_con_p);
        return;
    }
    //设置成非阻塞
    if(this->set_no_blocking(new_fd)==false){
        //设置失败也关掉吧
        close(new_fd);
        my_log * log=my_log::get_log_instance();
        log->write_log(LOG_ERR,errno,"接收连接后，设置非阻塞失败");
        add_connection_to_free(new_con_p);
        return ;
    }
    //填充new_con_p;
    new_con_p->fd=new_fd;
    new_con_p->listen=con_p->listen;    //由于是监听套接字，所以有这个变量
    new_con_p->opposite_addr=cli_addr;
    //绑定读写事件，用于后续回调
    new_con_p->event_read_handler=&_socket::recv_event_handler;
    new_con_p->event_write_handler=&_socket::write_event_handler;
    new_con_p->last_ping=time(NULL);
    //加入epoll中去
    epoll_event event;
    event.events=EPOLLIN|EPOLLRDHUP;    //第二个用于检测对端是否关闭（半关闭/全关闭）
    event.data.ptr=new_con_p;
    if(epoll_ctl(this->epoll_fd,EPOLL_CTL_ADD,new_fd,&event)==-1){
        //加入失败·，写日志
        my_log * log=my_log::get_log_instance(); 
        log->write_log(LOG_ERR,errno,"客户端连接加入epoll失败！！！");
        add_connection_to_free(new_con_p);
        return;
    }
    this->online_clients++;

    //加入成功，如果需要加入踢人时钟，那么这里也要加入踢人的最小堆结构相关的结构里，目前没有定义----现在定义了
    if(is_start_kick_map){
        this->add_con_to_kick_map(new_con_p);
    }   
    my_log::get_log_instance()->write_log(LOG_INFO,0,"成功接收一个连接ip:%s  port:%d  fd=%d",inet_ntoa(new_con_p->opposite_addr.sin_addr),ntohs(new_con_p->opposite_addr.sin_port),new_fd);
}


int _socket::doing_recv(connection_p con_p)
{
    //系统调用是可能被信号中断的
    int recv_res=recv(con_p->fd,con_p->precvbuf,con_p->precvlen,0);   // 读取数据
    if(recv_res<0){
        if(errno==EAGAIN|| errno== EWOULDBLOCK){
            //由于本来就是LT,应该不会出现这个错误
        my_log * log=my_log::get_log_instance();
        log->write_log(LOG_ALERT,errno,"调用recv接收数据 返回errno==EAGAIN|| errno== EWOULDBLOCK ！！！");
        return -1;
        }

        if(errno==EINTR){
            //由于LT,这次没收到数据，下次依旧会触发，所以也不用管
            my_log * log=my_log::get_log_instance();
            log->write_log(LOG_ALERT,errno,"调用recv接收数据 ,被信号中断了 ！！！");
            return -1;
        }

        //走到这里，我也将这个连接关闭掉
        my_log::get_log_instance()->write_log(LOG_INFO,0,"doing_recv时，将该连接  ip:%s port:%d 放入到待回收队列中",inet_ntoa(con_p->opposite_addr.sin_addr),ntohs(con_p->opposite_addr.sin_port));
        this->add_con_to_recycle(con_p);
        return -1;
    }
    if(recv_res==0){
        //对端已经关闭了，放入延迟回收队列中去
        this->add_con_to_recycle(con_p);
        return 0;
    }

    return recv_res;

}



//收包
void _socket::recv_event_handler(connection_p con_p)
{
    //一开始，我得去先接收包头
    if(con_p->cur_stat==PKG_HEAD_RECING){
        //收包头
        int recv_num=this->doing_recv(con_p);
        if(recv_num>0){
            //表示收到了一定的数据
            if(recv_num==con_p->precvlen){
                //包头收取完毕
                this->after_recv_header_complete(con_p);
                return;
            }else{
                //包头收取不完全
                con_p->precvbuf+=recv_num;
                con_p->precvlen-=recv_num;
                //等待下次触发，接收
                return;
            }

        }else{

        }


    }else{
        int recv_num=this->doing_recv(con_p);   //收包体
        if(recv_num>0){
            //表示收到了一定的数据
            if(recv_num==con_p->precvlen){
                //包体收取完毕
                this->after_recv_body_complete(con_p);
                return;
            }else{
                //包体收取不完全
                con_p->precvbuf+=recv_num;
                con_p->precvlen-=recv_num;
                //等待下次触发，接收
                return;
            }

        }

    }

    
}

void _socket::after_recv_header_complete(connection_p con_p)
{
    struct package_head_s * p_head=(struct package_head_s *)(con_p->package_head);
    unsigned short body_len=ntohs(p_head->pkg_body_len);    //网络字节序，转本地字节序
    //判断一下包体是否过长吧
    if(body_len>MAX_BODY_LEN){
        //说明是恶意包，那么就丢弃吧，然后将该连接恢复一下初始状态
        con_p->precvbuf=con_p->package_head;
        con_p->precvlen=sizeof(struct package_head_s);
        return;
    }
   //消息头
    struct message_head m_head;
    m_head.con_p=con_p;
    m_head.iCurrSequence=con_p->icurrentsequence;   //后续用于废包的判断

    int message_head_len=sizeof(struct message_head);   //消息头长度
    int package_head_len=sizeof(struct package_head_s); //包头长度
    char * message=new char[message_head_len+package_head_len+body_len]; //整条数据的长度-----不过这里如果new失败了，那就完蛋了，本机内存不够了，程序直接崩溃了，连日志都写不了
    con_p->recv_message_ptr=message;    //连接体里保存一下句柄
    memcpy(message,&m_head,message_head_len);   //放消息头
    message+=message_head_len;
    
    memcpy(message,p_head,package_head_len);
    message+=package_head_len;
    if(body_len==0){
        //包体数据为0，那么直接放入线程等待队列就好了
        this->after_recv_body_complete(con_p);
        return;
    }

    //更改接收状态
    con_p->precvbuf=message;
    con_p->precvlen=body_len;
    con_p->cur_stat=PKG_BOFY_RECING;    //接收包体
    
    

}


void _socket::after_recv_body_complete(connection_p con_p)
{
    //接收完包体后的处理逻辑
    con_p->wait_to_deal_received_pkgs++; //由于这已经是一个完整的包了，可以直接先++，避免造成误判该连接的包已经没了
    t_pool.add_message_to_list(con_p->recv_message_ptr);    //放入线程池等待队列中
    
    //恢复初始状态
    con_p->cur_stat=PKG_HEAD_RECING;
    con_p->precvbuf=con_p->package_head;
    con_p->precvlen=sizeof(struct package_head_s);
    con_p->recv_message_ptr=nullptr;
    // my_log::get_log_instance()->write_log(LOG_INFO,0,"成功收到一个包体,并已放入线程池的等待队列中");
}


void _socket::write_event_handler(connection_p con_p)
{
    //写事件处理函数，由于发送缓冲区不够了，需要通过事件触发来执行
    //干的事情就是发送信息，如果发送完毕，那么改变缓冲区状态标志
    int send_res=doing_send(con_p->fd,con_p->sendbuf,con_p->sendlen);
    if(send_res>0){
        if(send_res==con_p->sendlen){
            //发送完毕
            con_p->wait_to_send_count--;
            
            //将该fd在epoll中的EPOLLOUT事件给干掉
            change_epoll_event(con_p->fd,EPOLLIN|EPOLLRDHUP,con_p);

            
            con_p->send_buf_is_full_flag=SEND_BUF_NOT_FULL; //标志为空
            delete[] con_p->send_message_ptr;
            con_p->send_message_ptr=nullptr;
            pthread_cond_signal(&this->send_thread_cond);
            return;
        }else{
            con_p->sendbuf+=send_res;
            con_p->sendlen-=send_res;
            return;
        }
    }else if(send_res==-1){
        //几乎不会返回这个表示缓冲区满的标志，通知我缓冲区有空了，一发送，你又告诉我缓冲区满了？？？
        my_log * log=my_log::get_log_instance();
        log->write_log(LOG_STDERR,0,"通知我缓冲区有空，却返回缓冲区没空？？？");
        return;
    }else if(send_res==-2 || send_res==0){
        //出现奇怪的错误----可能对端关闭了，那么就等待相应的写事件触发，这里就不管了
        return;
    }

     
}
